/*
 *  PrintAst.cpp
 *  TempWorking
 *
 *  Created by Piotr Holc on 20/11/2010.
 *  Copyright 2010 Langerra Limited. All rights reserved.
 *
 */

#include <MAlice/Parser/location.hh>
#include <sstream>
#include <MAlice/Codegen.h>
#include <MAlice/Ast/PrintAst.h>
#include <MAlice/Ast/StringLiteralAst.h>
#include <MAlice/Ast/IntegerLiteralAst.h>
#include <MAlice/Ast/VariableAst.h>

namespace MAlice
{

PrintAst::PrintAst(location loc, ExpressionAst* expr) : ExpressionAst(loc),
	mExpr(expr)
{
	MALICE_TRACE;
}

PrintAst::~PrintAst()
{
	MALICE_TRACE;
}

llvm::Value* PrintAst::generate()
{
	MALICE_TRACE;
	Codegen *g = Codegen::get();
	Module *mod = g->getModule();
	Builder* builder = g->getBuilder();
	Function* printf = mod->getFunction("printf");

	Value* value = mExpr->generate();
	const Type* type = value->getType();

#define ZERO IntegerLiteralAst(0).generate()

	vector<Value*> args;
	if (type->isArrayTy() || type->isPointerTy())
	{
		// printf("%s", value);
		GlobalVariable* fmt = mod->getGlobalVariable(".fmt_s", true);
		GlobalVariable* str = new GlobalVariable(
								/*Module=*/      *mod,
								/*Type=*/        type,
								/*isConstant=*/  true,
								/*Linkage=*/     GlobalValue::PrivateLinkage,
								/*Initialiser=*/ cast<Constant>(value),
								/*Name=*/ Twine(""));
		str->setAlignment(1);

		ConstantInt* unbound = ConstantInt::get(Type::getInt64Ty(mod->getContext()), "0", 10);

		vector<Constant*> indicies;
		indicies.push_back(unbound);
		indicies.push_back(unbound);

		Constant* format = ConstantExpr::getGetElementPtr(fmt, &indicies[0], indicies.size());

		args.push_back(format);
		args.push_back(str);

	} else if (type->isIntegerTy(32))
	{
		// printf("%d", value);
		GlobalVariable* fmt = mod->getGlobalVariable(".fmt_d", true);
		ConstantInt* unbound = ConstantInt::get(Type::getInt64Ty(mod->getContext()), "0", 10);

		vector<Constant*> indicies;
		indicies.push_back(unbound);
		indicies.push_back(unbound);

		Constant* format = ConstantExpr::getGetElementPtr(fmt, &indicies[0], indicies.size());

		args.push_back(format);
		args.push_back(value);

	} else if (type->isIntegerTy(8))
	{
		// printf("'%c'", value);
		GlobalVariable* fmt = mod->getGlobalVariable(".fmt_c", true);
		ConstantInt* unbound = ConstantInt::get(Type::getInt64Ty(mod->getContext()), "0", 10);

		vector<Constant*> indicies;
		indicies.push_back(unbound);
		indicies.push_back(unbound);

		Constant* format = ConstantExpr::getGetElementPtr(fmt, &indicies[0], indicies.size());

		args.push_back(format);
		args.push_back(value);

	} else if (type->isIntegerTy(1))
	{
		// if (value == 0)
		//	printf("%s", "false");
		//else
		//	printf("%s", "true");
		GlobalVariable* fmt = mod->getGlobalVariable(".fmt_c", true);
		ConstantInt* unbound = ConstantInt::get(Type::getInt64Ty(mod->getContext()), "0", 10);

		vector<Constant*> indicies;
		indicies.push_back(unbound);
		indicies.push_back(unbound);

		Constant* format = ConstantExpr::getGetElementPtr(fmt, &indicies[0], indicies.size());

		args.push_back(format);
		args.push_back(value);

	} else
	{
		stringstream ss;
		ss << "Cannot print an expression with type '" << mod->getTypeName(
				value->getType())
				<< "' it could only be sentence, number or letter!" << endl;
		throw runtime_error(ss.str());
	}

#undef ZERO

	CallInst* results = builder->CreateCall(printf, args.begin(), args.end(), printf->getName());
	results->setCallingConv(CallingConv::C);

	return results;
}

}

